/*
 * Copyright (C) 2015 Michele Zanella <it.polimi.meteoCal>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.polimi.meteoCal.control;

import it.polimi.meteoCal.control.util.DateUtil;
import it.polimi.meteoCal.entity.BadWeather;
import it.polimi.meteoCal.entity.Event;
import it.polimi.meteoCal.entity.PrecipitationType;
import it.polimi.meteoCal.entity.Weather;
import it.polimi.meteoCal.entity.WeatherForecast;
import it.polimi.meteoCal.presentation.util.MessageUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Startup;
import javax.ejb.Singleton;
import net.aksingh.java.api.owm.DailyForecastData;
import net.aksingh.java.api.owm.DailyForecastData.Forecast;
import net.aksingh.java.api.owm.OpenWeatherMap;
import org.json.*;

/**
 *
 * @author Michele Zanella <it.polimi.meteoCal>
 */
@Startup
@Singleton
public class WeatherManager {

    public static final long MAX_DAY_FORECAST = 15;

    private static List<WeatherForecast> forecastList;

    /**
     *
     * @param place the event's place
     * @param startDate the event's starting date
     * @param endDate the event's ending date
     * @return a list with the retrieved forecast for each day of the event
     */
    public static List<WeatherForecast> retrieveForecast(String place, Date startDate, Date endDate) {
        try {
            OpenWeatherMap owm = new OpenWeatherMap(OpenWeatherMap.OWM_URL.PARAMETER_UNITS_VALUE_METRIC, "");

            byte forecastDays = DateUtil.upToDate(endDate);
            DailyForecastData dfd = owm.dailyForecastByCityName(place, forecastDays);

            System.out.println("Forecast for: " + place);
            forecastList = new ArrayList<>();

            for (Forecast f : dfd.getForecast_List()) {
                System.out.println("Day: " + f.getDateTime());
                System.out.println("Temperature: " + f.getTemperature_Object().getMaximumTemperature());
                System.out.println("Wind speed: " + f.getWindSpeed());
                System.out.println("Clouds coverage: " + f.getPercentageOfClouds());
                System.out.println("Precipitation Type: " + f.getWeather_List().get(0).getWeatherName());
            }
            for (Forecast f : dfd.getForecast_List()) {
                if (!DateUtil.isBeforeCalendar(f.getDateTime(), startDate)) {
                    WeatherForecast weatherForecast = new WeatherForecast();
                    weatherForecast.setWeather(createWeather(f));
                    forecastList.add(weatherForecast);
                }
            }
            return forecastList;
        } catch (IOException ex) {
            Logger.getLogger(WeatherManager.class.getName()).log(Level.SEVERE, null, ex);
            MessageUtil.addMessage(null, "Weather forecast not available");
            return new ArrayList<>();
        } catch (JSONException ex) {
            Logger.getLogger(WeatherManager.class.getName()).log(Level.SEVERE, null, ex);
            MessageUtil.addMessage(null, "Weather forecast not available");
            return new ArrayList<>();
        }
    }

    /**
     * This method find a date within 12 days after the event's starting date in
     * which the weather forecast are good according to the bad weather
     * specification
     *
     * @param event the event to be suggested
     * @return the suggested date to move the event
     */
    public static Date findGoodWeatherDate(Event event) {
        List<Forecast> forecasts;
        forecasts = retrieveSuggestions(event.getPlace());
        int eventPeriod = DateUtil.eventPeriod(event.getStartDate(), event.getEndDate());
        int count = 0;
        Date suggestedData = null;
        for (Forecast f : forecasts) {
            if (!createWeather(f).isWorst(event.getBadWeatherSpecification().getWeather()) && !DateUtil.isBeforeCalendar(f.getDateTime(), event.getStartDate())) {
                if (count == 0) {
                    suggestedData = f.getDateTime();
                }
                count++;
            } else {
                count = 0;
            }
            if (count == eventPeriod) {
                return suggestedData;
            }
        }
        return null;
    }

    private static List<Forecast> retrieveSuggestions(String place) {
        try {
            OpenWeatherMap owm = new OpenWeatherMap(OpenWeatherMap.OWM_URL.PARAMETER_UNITS_VALUE_METRIC, "");

            DailyForecastData dfd = owm.dailyForecastByCityName(place, (byte) MAX_DAY_FORECAST);

            System.out.println("Suggestion forecasts for: " + place);
            forecastList = new ArrayList<>();

            for (Forecast f : dfd.getForecast_List()) {
                System.out.println("Day: " + f.getDateTime());
                System.out.println("Temperature: " + f.getTemperature_Object().getMaximumTemperature());
                System.out.println("Wind speed: " + f.getWindSpeed());
                System.out.println("Clouds coverage: " + f.getPercentageOfClouds());
                System.out.println("Precipitation Type: " + f.getWeather_List().get(0).getWeatherName());
            }
            return dfd.getForecast_List();
        } catch (IOException ex) {
            Logger.getLogger(WeatherManager.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        } catch (JSONException ex) {
            Logger.getLogger(WeatherManager.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    private static Weather createWeather(Forecast forecast) {
        Weather weather = new Weather();
        weather.setCloudsCoverage((int) forecast.getPercentageOfClouds());
        weather.setTemperature((int) forecast.getTemperature_Object().getMaximumTemperature());
        weather.setWindSpeed((int) forecast.getWindSpeed());
        System.out.println(PrecipitationType.valueOf(forecast.getWeather_List().get(0).getWeatherName()));
        weather.setPrecipitationType(PrecipitationType.valueOf(forecast.getWeather_List().get(0).getWeatherName()));
        return weather;
    }

    /**
     *
     * @param e the event to be verified
     * @return true if an weather forecast for the given event is worst than the
     * bad weather specification defined by the user, false if there are all
     * good weather forecast
     */
    public static boolean verifyBadWeatherConditions(Event e) {
        BadWeather bw = e.getBadWeatherSpecification();
        List<WeatherForecast> forecastsList = e.getWeatherForecastList();
        for (WeatherForecast wf : forecastsList) {
            if (wf.getWeather().isWorst(bw.getWeather())) {
                return true;
            }
        }
        return false;
    }
}
